package com.huxi.lang.validation.predicate;

import static com.huxi.lang.local.DateAdapter.formatToText;
import static com.huxi.lang.local.DateAdapter.parseToDate;
import static com.huxi.lang.type.VTypeUtils.compare;
import static com.huxi.lang.type.VTypeUtils.parseNumber;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.Objects;
import java.util.UUID;

public abstract class Ge<T> implements VPredicate {

	abstract protected Object limit();

	public static NumberGe of(Number limit) {
		Objects.requireNonNull(limit);
		return new NumberGe(limit);
	}

	public static NumberGe of(long limit) {
		return new NumberGe(limit);
	}

	public static NumberGe of(int limit) {
		return new NumberGe(limit);
	}

	public static NumberGe of(double limit) {
		return new NumberGe(limit);
	}

	public static DateGe of(Date limit) {
		Objects.requireNonNull(limit);
		return new DateGe(limit);
	}

	public static StringGe of(String limit) {
		Objects.requireNonNull(limit);
		return new StringGe(limit);
	}

	public static class NumberGe extends Ge<Number> {

		private Number limit;

		NumberGe(Number limit) {
			this.limit = limit;
		}

		@Override
		protected Number limit() {
			return limit;
		}

		@Override
		public boolean test(Object t, boolean valueIfNull, boolean valueIfIllegalType) {
			if (t instanceof Number) {
				return compare((Number) t, limit) >= 0;
			}
			if (t instanceof CharSequence) {
				try {
					return compare(parseNumber(t.toString()), limit) >= 0;
				} catch (Exception e) {

				}
			}
			return t == null ? valueIfNull : valueIfIllegalType;
		}

		@Override
		public Lt.NumberLt negate() {
			return new Lt.NumberLt(limit);
		}

	}

	public static class DateGe extends Ge<Date> {
		private Date limit;

		DateGe(Date limit) {
			this.limit = limit;
		}

		@Override
		protected Date limit() {
			return limit;
		}

		@Override
		public boolean test(Object t, boolean valueIfNull, boolean valueIfIllegalType) {
			if (t instanceof Date) {
				return ((Date) t).compareTo(limit) >= 0;
			}
			if (t instanceof CharSequence) {
				try {
					return parseToDate(t.toString()).compareTo(limit) >= 0;
				} catch (Exception e) {
				}
			}
			if (t instanceof Long) {
				return new Date(((Number) t).longValue()).compareTo(limit) >= 0;
			}
			return t == null ? valueIfNull : valueIfIllegalType;
		}

		@Override
		public Lt.DateLt negate() {
			return new Lt.DateLt(limit);
		}
	}

	public static class StringGe extends Ge<String> {
		private String limit;

		StringGe(String limit) {
			this.limit = limit;
		}

		@Override
		protected String limit() {
			return limit;
		}

		@Override
		public boolean test(Object t, boolean valueIfNull, boolean valueIfIllegalType) {
			if (t instanceof CharSequence || t instanceof Number) {
				return t.toString().compareTo(limit) >= 0;
			}
			if (t instanceof Date) {
				return formatToText((Date) t).compareTo(limit) >= 0;
			}
			if (t instanceof LocalDate || t instanceof LocalDateTime || t instanceof UUID) {
				return t.toString().compareTo(limit) >= 0;
			}
			return t == null ? valueIfNull : valueIfIllegalType;
		}

		@Override
		public Lt.StringLt negate() {
			return new Lt.StringLt(limit);
		}
	}

}
